#pragma once

#ifndef Referenced_h
#define Referenced_h

#include <new>

static const long ALREADY_FREED = 0x82345678;

namespace fusion
{
    namespace referenceddetail
    {
        class IUnknown_Base
        {
        public:
            virtual ~IUnknown_Base() {}
            virtual void DestroyInstance() const
            {
                delete this;
            }
        };

        class IUnknown_Virtual
        {
        public:
            virtual ~IUnknown_Virtual() {}
            virtual void DestroyInstance() const
            {
                delete this;
            }

            virtual long AddRef() const = 0;
            virtual long Release() const = 0;
            virtual bool IsUniquelyReferenced() const = 0;
        };

        template <class BaseT>
        class ReferencedBase : private BaseT
        {
        public:
            // ----------------------------------
            // Member functions
            // ----------------------------------
            long AddRef() const
            {
                return ++m_ulRefCount;
            }

            long Release() const
            {
                long ulReturn = --m_ulRefCount;

                if (ulReturn == 0)
                {
                    //
                    // Setting the ref count to a special, non-zero value so that we can see
                    // when an object is in the "deleting" state, and spurious ref-counts during
                    // destruction wont cause problems.
                    //
                    // It's actually possible to hit this line more than once for the same object:
                    //
                    // 1) The destructor may call code which adds and removes a reference (setting the refcount to 1 then
                    //    back to 0)
                    // 2) If another thread has access to a non-counted pointer to this object, then that thread may
                    //    increment then decrement the reference count.
                    //
                    // The assignment to a magic (non-zero) value fixes case (1).
                    //
                    // Case (2) is completely bogus. *Nobody* should be accessing a pointer to an object without
                    // proper reference counting. Thus, if the ref-count drops to zero, then by definition
                    // nobody should have a valid pointer to this object anymore (except, the destructor).
                    //
                    // Case (2) Exception:  The NativeWeakIdTable keeps a non-ref counted pointers to ref-counted objects.
                    //                      There is a race between the refcount dropping to zero and the object
                    //                      being removed from the table. This is resolved by looking at the ref count.
                    //                      If the refcount is less or equal to zero the handle table considers the object
                    //                      to be released.
                    //                      It uses a Mutex to ensure that it never references memory already released.
                    //                      IT IS IMPORTANT THAT THE SPECIAL VALUE HERE IS NEGATIVE.
                    m_ulRefCount = ALREADY_FREED;

                    // Now, what we _really_ wanted to do here in the first place...
                    this->DestroyInstance();
                }

                return ulReturn;
            }

            bool IsUniquelyReferenced() const
            {
                return m_ulRefCount == 1;
            }

        protected:
            // ----------------------------------
            // Constructors / Destructor
            // ----------------------------------
            ReferencedBase() 
                : m_ulRefCount(0)
            {
            }

            virtual ~ReferencedBase()
            {
            }

            // ----------------------------------
            // Member variables
            // ----------------------------------
        private:
            mutable volatile long    m_ulRefCount;
        };
    }

    class NonVirtualReferenced : public referenceddetail::ReferencedBase<referenceddetail::IUnknown_Base>
    {
    public:
    protected:
        virtual ~NonVirtualReferenced() {}
    };

    class Referenced : public referenceddetail::ReferencedBase<referenceddetail::IUnknown_Virtual>
    {
    public:
    protected:
        virtual ~Referenced() {}
    };


    // This is a utility class/bag of functions to ease AddRef'ing and Releasing pointers where you don't 
    // know if the pointer is to a Referenced object or not. It abuses varargs syntax (like printf) and templates
    // to figure out at compile time if the type being passed in is Referenced
    //
    // To use:
    //      class Foo { };
    //      Foo* f = new Foo();
    //      RefWrapper_TypeSafe<Foo*>::AddRef(f); // since Foo isn't Referenced, this is a no-op

    //      class Bar : public Referenced { };
    //      Bar* b = new Bar();
    //      RefWrapper_TypeSafe<Bar*>::AddRef(b); // Bar _is_ Referenced. The compiler will convert this line to b->AddRef()

    //      template <class TPtr>
    //      class Thinger 
    //      {
    //          Thinger(TPtr pClass) : m_p(pClass) 
    //          {
    //              RefWrapper_TypeSafe<TPtr>::AddRef(m_p); // we don't know if this is Referenced or not, but the compiler will at compile time
    //          }
    //          TPtr m_p;
    //      };
    template <typename TPtr>
    class RefWrapper_TypeSafe
    {
    public:
        // intentionally returning void so that compiler can make no-ops out of non-ref'd
        // implementations
        inline static void AddRef(const TPtr pClass)
        {
            AddRef_Inner(pClass);
        }

        // intentionally returning void so that compiler can make no-ops out of non-ref'd
        // implementations
        inline static void Release(const TPtr pClass)
        {
            Release_Inner(pClass);
        }

        inline static bool IsReferenced(const TPtr pClass)
        {
            return IsReferenced_Inner(pClass);
        }

    private:
        inline static void Release_Inner(...) 
        {
            // uncomment line below to see what the compiler is making for non-ref'd types
            //#pragma message("Not ref'd: " __FUNCTION__)
        }
        inline static void Release_Inner(const Referenced* pClass)
        {
            // uncomment line below to see what the compiler is making ref'd
            //#pragma message("Ref'd: " __FUNCTION__)

            ASSERT(pClass);
            pClass->Release();
        }

        inline static void AddRef_Inner(...) { }
        inline static void AddRef_Inner(const Referenced* pClass)
        {
            ASSERT(pClass);
            pClass->AddRef();
        }

        inline static bool IsReferenced_Inner(...)
        {
            return false;
        }
        inline static bool IsReferenced_Inner(const Referenced* pClass)
        {
            return true;
        }
    };
}

#endif // Referenced_h
